#include "ServerGame.h"

unsigned int ServerGame::client_id;

ServerGame::ServerGame(void)
{
	// id's to assign clients for our table
	client_id = 0;

	// set up the server network to listen 
	network = new ServerNetwork();
	terrainData = new TerrainGenerationData();

	pINet = new PlayerInformationNetworking(MAX_NUM_PLAYERS_PER_SESSION);
}

void ServerGame::update() {

	// get new clients
	if (network->acceptNewClient(client_id)) {
		printf("client %d has been connected to the server\n", client_id);

		client_id++;
	}

	receiveFromClients();
}

void ServerGame::receiveFromClients() {
	// go through all clients
	std::map<unsigned int, SOCKET>::iterator iter;
	char arr[PACKET_SIZE];

	for (iter = network->sessions.begin(); iter != network->sessions.end(); iter++) {
		Packet packet;
		// get data for that client
		int data_length = network->receiveData(iter->first, network_data);
		unsigned int idClient = iter->first;

		if (data_length <= 0) {
			//no data recieved
			continue;
		}

		int i = 0;
		while (i < (unsigned int)data_length) {
			packet.deserialize(&(network_data[i]));
			i += sizeof(Packet);
			memcpy(arr, packet.data, PACKET_SIZE);

			switch (packet.packet_type) {
			case INIT_CONNECTION: {
				printf("server received init packet from client %d \n", idClient);
				numPlayersConnected = network->sessions.size();
				if (numPlayersConnected == MAX_NUM_PLAYERS_PER_SESSION) {
					sendInitialDataToClients();
				}
				else {
					printf("Not all clients connected, packet sent to clients.\n");
					sendActionPackets(NOT_ALL_CLIENTS_CONNECTED);
				}
				break;
			}
			case SERVER_TO_CLIENT_UPDATE_TERRAIN_DATA: {
				printf("server received request for update packet from client\n");
				//recompute the positions of trees and spawn point and send those
				terrainData->updateTreesAndSpawnerPositions();
				sendUpdateDataToClient(idClient);
				break;
			}
			case PLAYER_STATUS: {
				printf("server received player update from client nr %d \n", idClient);
				//decode the data sent by the client
				PlayerDataNetworking* pDNet = DecodePlayerData(&packet);
				pDNet->setPlayerId(idClient);
				//set it in the data for players
				pINet->setPlayerDataNetworkingAt(idClient, *pDNet);
				//send the data back with all the players but the current one
				Packet* p = EncodePlayerInformation(pINet);
				network->sendToAll((char*)p, sizeof(Packet));
				break;
			}
			default:
				printf("error in packet types\n");
				break;
			}
		}
	}
}

void ServerGame::sendActionPackets(PacketTypes TYPE) {
	// send action packet
	const unsigned int packet_size = sizeof(Packet);
	char packet_data[packet_size];

	Packet packet;
	packet.packet_type = TYPE;

	packet.serialize(packet_data);
	network->sendToAll(packet_data, packet_size);
}

void ServerGame::sendInitialDataToClients() {
	const unsigned int actualPacketSize = PACKET_SIZE + sizeof(PacketTypes) + sizeof(int);
	char packet_data[actualPacketSize];
	//put the data inside packet_data
	void* currentPtr = packet_data;
	{
		Packet currentPacket;
		currentPacket.packet_type = INIT_SERVER_TO_CLIENT_DATA;
		unsigned int terrainSize = sizeof(TerrainGenerationData);
		//the actual length equals to the actual size of the terraindata object and the size of memory all the pointers pointing to.
		currentPacket.packet_length = terrainSize + (terrainData->getNumTrees() * (sizeof(Vector3) + sizeof(int)));
		//copy all the data of the terrainData object to the packet's data
		memcpy(currentPacket.data, terrainData, terrainSize);
		//then copy the tree positions there
		currentPtr = (char*)(&(currentPacket.data)) + terrainSize;
		memcpy(currentPtr, terrainData->getTreePositions(), terrainData->getNumTrees() * sizeof(Vector3));
		//then the treesizes
		currentPtr = (char*)currentPtr + terrainData->getNumTrees() * sizeof(Vector3);
		memcpy(currentPtr, terrainData->getTreeSizes(), terrainData->getNumTrees() * sizeof(int));

		//the packet is ready, then just copy the memory in the packet into the packet_data
		memcpy(packet_data, &currentPacket, actualPacketSize);
	}

	network->sendToAll(packet_data, actualPacketSize);
}

void ServerGame::sendUpdateDataToClient(unsigned int clientId){
	const unsigned int actualPacketSize = PACKET_SIZE + sizeof(PacketTypes)+sizeof(int);
	char packet_data[actualPacketSize];
	//put the data inside packet_data
	void* currentPtr = packet_data;
	{
		Packet currentPacket;
		currentPacket.packet_type = SERVER_TO_CLIENT_UPDATE_TERRAIN_DATA;
		//we need to only send over two things, first is the tree positions, the second is the spawner position
		//first send over the tree positon.
		currentPtr = currentPacket.data;
		memcpy(currentPtr, terrainData->getTreePositions(), terrainData->getNumTrees() * sizeof(Vector3));
		currentPtr = (char*)currentPtr + terrainData->getNumTrees() * sizeof(Vector3);
		//then send over the spawner position.
		memcpy(currentPtr, &(terrainData->getSpanwerPosition()), sizeof(Vector3));

		currentPacket.packet_length = terrainData->getNumTrees() * sizeof(Vector3) + sizeof(Vector3);

		//the packet is ready, then just copy the memory in the packet into the packet_data
		memcpy(packet_data, &currentPacket, actualPacketSize);
	}

	network->sendDataToOneClient(packet_data, actualPacketSize, clientId);
}

PlayerDataNetworking* ServerGame::DecodePlayerData(const Packet* packet)
{
	PlayerDataNetworking* pDN = new PlayerDataNetworking();
	memcpy(pDN, packet->data, sizeof(PlayerDataNetworking));
	return pDN;
}

Packet* ServerGame::EncodePlayerInformation(const PlayerInformationNetworking* pInfoNw)
{
	Packet* currentPacket = new Packet();
	currentPacket->packet_type = PLAYER_INFO;
	int numberPlayer = pInfoNw->GetNumPlayers();

	currentPacket->packet_length = sizeof(int) + numberPlayer * sizeof(PlayerDataNetworking);
	memcpy(currentPacket->data, (&numberPlayer), sizeof(int));
	memcpy(currentPacket->data + sizeof(int), pInfoNw->GetPlayerDataNetworkings(), numberPlayer * sizeof(PlayerDataNetworking));

	return currentPacket;
}